package io.gitee.zhangbinhub.admin.oauth.controller.api

import io.gitee.zhangbinhub.acp.boot.exceptions.ServerException
import io.gitee.zhangbinhub.acp.boot.interfaces.LogAdapter
import io.gitee.zhangbinhub.acp.boot.vo.ErrorVo
import io.gitee.zhangbinhub.admin.common.api.OauthApi
import io.gitee.zhangbinhub.admin.common.base.BaseController
import io.gitee.zhangbinhub.admin.common.permission.BaseExpression
import io.gitee.zhangbinhub.admin.common.vo.InfoVo
import io.gitee.zhangbinhub.admin.oauth.component.AuthTokenService
import io.gitee.zhangbinhub.admin.oauth.domain.ApplicationDomain
import io.gitee.zhangbinhub.admin.oauth.domain.UserDomain
import io.gitee.zhangbinhub.admin.oauth.vo.OnlineInfoVo
import io.swagger.annotations.*
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*
import springfox.documentation.annotations.ApiIgnore
import javax.validation.constraints.NotEmpty
import javax.validation.constraints.NotNull

/**
 * @author zhang by 08/03/2019
 * @since JDK 11
 */
@Validated
@RestController
@RequestMapping(OauthApi.basePath)
@Api(tags = ["认证信息"])
class TokenController @Autowired
constructor(
    private val logAdapter: LogAdapter,
    private val applicationDomain: ApplicationDomain,
    private val userDomain: UserDomain,
    private val authTokenService: AuthTokenService
) : BaseController(logAdapter) {

    @ApiOperation(value = "注销当前用户")
    @PostMapping(value = [OauthApi.logOut], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(ServerException::class)
    fun doLogOut(@ApiIgnore bearerTokenAuthentication: BearerTokenAuthentication): ResponseEntity<InfoVo> =
        try {
            authTokenService.removeByToken(bearerTokenAuthentication.token.tokenValue)
            logAdapter.info("用户[loginNo=" + bearerTokenAuthentication.name + "]主动下线!")
            ResponseEntity.ok(InfoVo(message = "成功下线"))
        } catch (e: Exception) {
            throw ServerException(e.message)
        }

    @ApiOperation(value = "获取各应用在线用户数统计")
    @GetMapping(value = [OauthApi.onlineInfo], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(ServerException::class)
    fun getOnlineInfo(@ApiIgnore bearerTokenAuthentication: BearerTokenAuthentication): ResponseEntity<List<OnlineInfoVo>> =
        try {
            mutableListOf<OnlineInfoVo>().apply {
                applicationDomain.getOwnAppList(bearerTokenAuthentication).forEach {
                    this.add(
                        OnlineInfoVo(
                            appId = it.id,
                            appName = it.appName,
                            count = authTokenService.findTokensByClientId(it.id).size.toLong()
                        )
                    )
                }
            }.let {
                ResponseEntity.ok(it)
            }
        } catch (e: Exception) {
            throw ServerException(e.message)
        }

    @ApiOperation(value = "获取用户在线情况")
    @ApiResponses(ApiResponse(code = 400, message = "参数校验不通过，找不到用户信息；", response = ErrorVo::class))
    @PreAuthorize(BaseExpression.superOnly)
    @GetMapping(value = [OauthApi.onlineInfo + "/{userId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(ServerException::class)
    fun getOnlineInfo(
        @ApiIgnore bearerTokenAuthentication: BearerTokenAuthentication,
        @ApiParam(value = "用户id", required = true)
        @PathVariable
        userId: String
    ): ResponseEntity<List<OnlineInfoVo>> =
        try {
            mutableListOf<OnlineInfoVo>().apply {
                val userInfo = userDomain.getUserInfoById(userId) ?: throw ServerException("找不到该用户信息")
                applicationDomain.getOwnAppList(bearerTokenAuthentication).forEach { item ->
                    this.add(
                        OnlineInfoVo(
                            appId = item.id,
                            appName = item.appName,
                            count = authTokenService.findTokensByClientIdAndUserName(
                                item.id,
                                userInfo.loginNo
                            ).size.toLong()
                        )
                    )
                }
            }.let {
                ResponseEntity.ok(it)
            }
        } catch (e: Exception) {
            throw ServerException(e.message)
        }

    @ApiOperation(value = "指定应用下的用户强制下线")
    @ApiResponses(ApiResponse(code = 400, message = "参数校验不通过；没有权限做此操作；", response = ErrorVo::class))
    @PreAuthorize(BaseExpression.superOnly)
    @DeleteMapping(value = [OauthApi.onlineInfo + "/{appId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(ServerException::class)
    fun delete(
        @ApiParam(value = "应用id", required = true)
        @PathVariable
        appId: String,
        @ApiParam(value = "用户id列表", required = true)
        @NotEmpty(message = "id不能为空")
        @NotNull(message = "id不能为空")
        @RequestBody
        idList: List<String>
    ): ResponseEntity<InfoVo> =
        try {
            idList.forEach {
                val userInfo = userDomain.getUserInfoById(it) ?: throw ServerException("找不到该用户信息")
                authTokenService.removeTokensByClientIdAndUserName(appId, userInfo.loginNo)
                logAdapter.info("用户[" + userInfo.name + "(" + userInfo.loginNo + ")]被管理员强制下线!")
            }
            ResponseEntity.ok(InfoVo(message = "成功下线"))
        } catch (e: Exception) {
            throw ServerException(e.message)
        }

}
